Predição de preços de atacantes em R


# Setup Célula oculta para o setup:

Obtenção de Dados

Os dados são obtidos de um servidor SQL ou csv e armazenados na variável df

df <- read_csv("/media/njaneto/HD1/FIAP/PROGRAMANDO_IA_COM_R/fifa18-data-analysis/model/data/fifa18.csv", locale = locale(encoding = "ISO-8859-1"))
New names:
* `` -> ...1
Registered S3 method overwritten by 'cli':
  method     from
  print.tree tree
Rows: 17994 Columns: 58
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (9): name, full_name, club, league, nationality, Position, work_rate_att, work_rate_def, preferred_foot
dbl (49): ...1, ID, special, eur_value, eur_wage, eur_release_clause, crossing, finishing, heading_accuracy, short_...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
setDT(df)

Análise dos dados

Visualização inicial dos dados

Quantidade de linhas e colunas obtidas:

dim(df)
[1] 17994    58

Primeiros registros obtidos:

head(df)

Incluindo coluna de continente

Africa<-c('Algeria','Angola','Benin','Botswana','Burkina','Burundi','Cameroon','Cape Verde','Central African Republic','Chad','Comoros','Congo','Congo Democratic Republic of','Djibouti','Egypt','Equatorial Guinea','Eritrea','Ethiopia','Gabon','Gambia','Ghana','Guinea','Guinea-Bissau','Ivory Coast','Kenya','Lesotho','Liberia','Libya','Madagascar','Malawi','Mali','Mauritania','Mauritius','Morocco','Mozambique','Namibia','Niger','Nigeria','Rwanda','Sao Tome and Principe','Senegal','Seychelles','Sierra Leone','Somalia','South Africa','South Sudan','Sudan','Swaziland','Tanzania','Togo','Tunisia','Uganda','Zambia','Zimbabwe','Burkina Faso')
Antarctica<-c('Fiji','Kiribati','Marshall Islands','Micronesia','Nauru','New Zealand','Palau','Papua New Guinea','Samoa','Solomon Islands','Tonga','Tuvalu','Vanuatu')
Asia<-c('Afghanistan','Bahrain','Bangladesh','Bhutan','Brunei','Burma (Myanmar)','Cambodia','China','East Timor','India','Indonesia','Iran','Iraq','Israel','Japan','Jordan','Kazakhstan','North Korea','South Korea','Kuwait','Kyrgyzstan','Laos','Lebanon','Malaysia','Maldives','Mongolia','Nepal','Oman','Pakistan','Philippines','Qatar','Russian Federation','Saudi Arabia','Singapore','Sri Lanka','Syria','Tajikistan','Thailand','Turkey','Turkmenistan','United Arab Emirates','Uzbekistan','Vietnam','Yemen','Russia')
Europe<-c('Albania','Andorra','Armenia','Austria','Azerbaijan','Belarus','Belgium','Bosnia and Herzegovina','Bulgaria','Croatia','Cyprus','Czech Republic','Denmark','Estonia','Finland','France','Georgia','Germany','Greece','Hungary','Iceland','Ireland','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Romania','San Marino','Scotland','Serbi','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','England','Vatican City','Republic of Ireland','Wales')
North_america<-c('Antigua and Barbuda','Bahamas','Barbados','Belize','Canada','Costa Rica','Cuba','Dominica','Dominican Republic','El Salvador','Grenada','Guatemala','Haiti','Honduras','Jamaica','Mexico','Nicaragua','Panama','Saint Kitts and Nevis','Saint Lucia','Saint Vincent and the Grenadines','Trinidad and Tobago','United States')
South_america<-c('Argentina','Bolivia','Brazil','Chile','Colombia','Ecuador','Guyana','Paraguay','Peru','Suriname','Uruguay','Venezuela')

df[, continent:= df$nationality]
df <- df %>% relocate(continent, .after = nationality)

df$continent[df$continent %in% Africa ] <- "Africa"
df$continent[df$continent %in% Antarctica ] <- "Antarctica"
df$continent[df$continent %in% Asia ] <- "Asia"
df$continent[df$continent %in% Europe ] <- "Europe"
df$continent[df$continent %in% North_america ] <- "North_america"
df$continent[df$continent %in% South_america ] <- "South_america"

Graficos de analise dos dados

Validação incial de dados

plot_intro(df)

plot_missing(df)

Jogadores por pais

pais <- df[,.N, by=nationality]
fr <- joinCountryData2Map(dF=pais, joinCode = "NAME", nameJoinColumn = "nationality", verbose = F)
148 codes from your data successfully matched countries in the map
16 codes from your data failed to match with a country code in the map
95 codes from the map weren't represented in your data
mapCountryData(mapToPlot = fr,nameColumnToPlot = "N",catMethod = "fixedWidth",
               oceanCol = "steelblue1",missingCountryCol = "white",
               mapTitle = "Jogadores por pais",
               aspect = "variable") 
Warning in plot.window(xlim = xlim, ylim = ylim, asp = aspect) :
  NAs introduzidos por coerção

Filtrando somente atacantes

selecionando os atacantes

FORWARD_EUROPE <- df %>%
  filter(Position == "Forward" & continent == "Europe")

head(FORWARD_EUROPE)

selecionando os goleiros da variável

#-- base para validacao
FORWARD_NOT_EUROPE <- df %>%
  filter(Position == "Forward" & continent != "Europe")

head(FORWARD_NOT_EUROPE)

Removendo dados

removendo os NAs e variaves nao numericas da base de treino

FORWARD_EUROPE <- FORWARD_EUROPE %>% 
  select_if(~ !any(is.na(.))) %>%
  select_if(~ any(is.numeric(.)))

head(FORWARD_EUROPE)

Removendo os NAs e variaves nao numericas da base de validacao

fifa.18.fw <- FORWARD_NOT_EUROPE %>% 
  select_if(~ !any(is.na(.))) %>%
  select_if(~ any(is.numeric(.)))

head(fifa.18.fw)

Treinamento e predicao

Boxplot

Grafico de boxplot simples

boxplot(FORWARD_EUROPE)

NA
NA

Correlacao

Grafico de Correlacao simples

corrMatrix <- cor(FORWARD_EUROPE)
corrplot.mixed(corrMatrix, 
               lower = "ellipse", 
               upper = "number",
               tl.pos = "lt",
               tl.col = "black",
               order="hclust",
               hclust.method = "ward.D",
               addrect = 3)

Definição do modelo de ML

Modelo com Floresta

set.seed(1)
reg.test <- randomForest(formula = eur_value ~ ., 
                         data = FORWARD_EUROPE, 
                         ntree=100, 
                         proximity=TRUE, 
                         localImp=TRUE)
plot(reg.test)

Predição

Avaliação

Predição dos preços dos goleiros

predito = predict(reg.test, FORWARD_EUROPE)
print(paste("R2: ", R2_Score(predito, FORWARD_EUROPE$eur_value) ) )
[1] "R2:  0.987388762384877"
print(paste("MSE: ", MSE(predito, FORWARD_EUROPE$eur_value) ) )
[1] "MSE:  549036621866.559"
FORWARD_EUROPE[, predito:=predito]
FORWARD_EUROPE <- FORWARD_EUROPE %>% relocate(predito, .after = eur_value)
head(FORWARD_EUROPE)

Dispersão

Avaliaçao de acerto X erro do modelo

FORWARD_EUROPE %>%
  mutate(predito = predict(reg.test, .)) %>%
  plot_ly(x = ~eur_value,
          y= ~predito,
          type='scatter',
          mode='markers',
          text=~paste0("Real value: ", currency(eur_value, symbol='€', digits = 0L), 
                       "\nPredicted value: ", currency(predito, symbol='€', digits = 0L), 
                       "\nError: ", (eur_value - predito)),
          name="Dispersão") %>%
  add_segments(x=0, y=0, xend = 100000000, yend = 100000000, name="Equilíbrio")

Avaliação II

Avaliação dos preços (baseado nos dados que nunca viu antes!)

Predição dos preços dos goleiros

predito = predict(reg.test, fifa.18.fw)
print(paste("R2: ", R2_Score(predito, fifa.18.fw$eur_value) ) )
[1] "R2:  0.959228507730088"
print(paste("MSE: ", MSE(predito, fifa.18.fw$eur_value) ) )
[1] "MSE:  1819033438223.35"
fifa.18.fw[, predito:=predito]
fifa.18.fw <- fifa.18.fw %>% relocate(predito, .after = eur_value)
head(fifa.18.fw)

Dispersão

fifa.18.fw %>%
  mutate(predito = predict(reg.test, .)) %>%
  plot_ly(x = ~eur_value,
          y= ~predito,
          type='scatter',
          mode='markers',
          text=~paste0("Real value: ", currency(eur_value, symbol='€', digits = 0L), 
                       "\nPredicted value: ", currency(predito, symbol='€', digits = 0L), 
                       "\nError: ", (eur_value - predito)),
          name="Dispersão") %>%
  add_segments(x=0, y=0, xend = 100000000, yend = 100000000, name="Equilíbrio")

Resultado

resultado final com os goleiros e seus valores preditos

output <- FORWARD_NOT_EUROPE %>%
  select(Position, name, eur_value) 

output[, eur_value := currency(fifa.18.fw$eur_value, symbol = '€', digits = 0L)]
output[, 'Preço "Calculado" (€)' := currency(fifa.18.fw$predito, symbol = '€', digits = 0L)]
output[, 'Potencial Valorização (€)' := currency((fifa.18.fw$predito - fifa.18.fw$eur_value), symbol='€', digits = 0L) ]
output[, 'Potencial Valorização (%)' := (percent((fifa.18.fw$predito - fifa.18.fw$eur_value) / 100000000)) ]

output <- output %>% 
  rename(
    'Posição' = Position,
    'Jogador' = name,
    'Preço de mercado' = eur_value
  )

head(output)
NA

Rodapé

Case de Advanced Analytics


Davidson Mizael - São Paulo - 2021

LS0tCnRpdGxlOiAiQ2FzZSBkZSBBZHZhbmNlZCBBbmFseXRpY3MiCmF1dGhvcjogIkRhdmlkc29uIE1pemFlbCIKc3VidGl0bGU6ICdBbmFsaXNlIHBhcmEgZGV0ZXJtaW5hciBvcyBBdGFjYW50ZSBjb20gbWFpb3IgcG90ZW5jaWFsIGN1c3RvLWJlbmVmaWNpbyAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDEwCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCiMgUHJlZGnDp8OjbyBkZSBwcmXDp29zIGRlIGF0YWNhbnRlcyBlbSBSCiFbXShpbWcvYXRhY2FudGUuanBnKXt3aWR0aD0xMDAlfSAgCiMgU2V0dXAKQ8OpbHVsYSBvY3VsdGEgcGFyYSBvIHNldHVwOgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Kcm0obGlzdD1scygpKQpsaWJyYXJ5KERCSSkKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodHJlZSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoTUxtZXRyaWNzKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZm9ybWF0dGFibGUpCiNsaWJyYXJ5KFJQb3N0Z3JlcykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShEYXRhRXhwbG9yZXIpCmxpYnJhcnkocndvcmxkbWFwKQpvcHRpb25zKHNjaXBlbiA9IDk5OSwgZGlnaXRzID0gNCkKYGBgCgojIE9idGVuw6fDo28gZGUgRGFkb3MKT3MgZGFkb3Mgc8OjbyBvYnRpZG9zIGRlIHVtIHNlcnZpZG9yIFNRTCBvdSBjc3YgZSBhcm1hemVuYWRvcyBuYSB2YXJpw6F2ZWwgYGRmYAoKYGBge3J9CmRmIDwtIHJlYWRfY3N2KCIvbWVkaWEvbmphbmV0by9IRDEvRklBUC9QUk9HUkFNQU5ET19JQV9DT01fUi9maWZhMTgtZGF0YS1hbmFseXNpcy9tb2RlbC9kYXRhL2ZpZmExOC5jc3YiLCBsb2NhbGUgPSBsb2NhbGUoZW5jb2RpbmcgPSAiSVNPLTg4NTktMSIpKQpzZXREVChkZikKYGBgCiMgQW7DoWxpc2UgZG9zIGRhZG9zCgojIyBWaXN1YWxpemHDp8OjbyBpbmljaWFsIGRvcyBkYWRvcwpRdWFudGlkYWRlIGRlIGxpbmhhcyBlIGNvbHVuYXMgb2J0aWRhczoKYGBge3J9CmRpbShkZikKYGBgClByaW1laXJvcyByZWdpc3Ryb3Mgb2J0aWRvczoKYGBge3J9CmhlYWQoZGYpCmBgYApJbmNsdWluZG8gY29sdW5hIGRlIGNvbnRpbmVudGUKYGBge3J9CkFmcmljYTwtYygnQWxnZXJpYScsJ0FuZ29sYScsJ0JlbmluJywnQm90c3dhbmEnLCdCdXJraW5hJywnQnVydW5kaScsJ0NhbWVyb29uJywnQ2FwZSBWZXJkZScsJ0NlbnRyYWwgQWZyaWNhbiBSZXB1YmxpYycsJ0NoYWQnLCdDb21vcm9zJywnQ29uZ28nLCdDb25nbyBEZW1vY3JhdGljIFJlcHVibGljIG9mJywnRGppYm91dGknLCdFZ3lwdCcsJ0VxdWF0b3JpYWwgR3VpbmVhJywnRXJpdHJlYScsJ0V0aGlvcGlhJywnR2Fib24nLCdHYW1iaWEnLCdHaGFuYScsJ0d1aW5lYScsJ0d1aW5lYS1CaXNzYXUnLCdJdm9yeSBDb2FzdCcsJ0tlbnlhJywnTGVzb3RobycsJ0xpYmVyaWEnLCdMaWJ5YScsJ01hZGFnYXNjYXInLCdNYWxhd2knLCdNYWxpJywnTWF1cml0YW5pYScsJ01hdXJpdGl1cycsJ01vcm9jY28nLCdNb3phbWJpcXVlJywnTmFtaWJpYScsJ05pZ2VyJywnTmlnZXJpYScsJ1J3YW5kYScsJ1NhbyBUb21lIGFuZCBQcmluY2lwZScsJ1NlbmVnYWwnLCdTZXljaGVsbGVzJywnU2llcnJhIExlb25lJywnU29tYWxpYScsJ1NvdXRoIEFmcmljYScsJ1NvdXRoIFN1ZGFuJywnU3VkYW4nLCdTd2F6aWxhbmQnLCdUYW56YW5pYScsJ1RvZ28nLCdUdW5pc2lhJywnVWdhbmRhJywnWmFtYmlhJywnWmltYmFid2UnLCdCdXJraW5hIEZhc28nKQpBbnRhcmN0aWNhPC1jKCdGaWppJywnS2lyaWJhdGknLCdNYXJzaGFsbCBJc2xhbmRzJywnTWljcm9uZXNpYScsJ05hdXJ1JywnTmV3IFplYWxhbmQnLCdQYWxhdScsJ1BhcHVhIE5ldyBHdWluZWEnLCdTYW1vYScsJ1NvbG9tb24gSXNsYW5kcycsJ1RvbmdhJywnVHV2YWx1JywnVmFudWF0dScpCkFzaWE8LWMoJ0FmZ2hhbmlzdGFuJywnQmFocmFpbicsJ0JhbmdsYWRlc2gnLCdCaHV0YW4nLCdCcnVuZWknLCdCdXJtYSAoTXlhbm1hciknLCdDYW1ib2RpYScsJ0NoaW5hJywnRWFzdCBUaW1vcicsJ0luZGlhJywnSW5kb25lc2lhJywnSXJhbicsJ0lyYXEnLCdJc3JhZWwnLCdKYXBhbicsJ0pvcmRhbicsJ0themFraHN0YW4nLCdOb3J0aCBLb3JlYScsJ1NvdXRoIEtvcmVhJywnS3V3YWl0JywnS3lyZ3l6c3RhbicsJ0xhb3MnLCdMZWJhbm9uJywnTWFsYXlzaWEnLCdNYWxkaXZlcycsJ01vbmdvbGlhJywnTmVwYWwnLCdPbWFuJywnUGFraXN0YW4nLCdQaGlsaXBwaW5lcycsJ1FhdGFyJywnUnVzc2lhbiBGZWRlcmF0aW9uJywnU2F1ZGkgQXJhYmlhJywnU2luZ2Fwb3JlJywnU3JpIExhbmthJywnU3lyaWEnLCdUYWppa2lzdGFuJywnVGhhaWxhbmQnLCdUdXJrZXknLCdUdXJrbWVuaXN0YW4nLCdVbml0ZWQgQXJhYiBFbWlyYXRlcycsJ1V6YmVraXN0YW4nLCdWaWV0bmFtJywnWWVtZW4nLCdSdXNzaWEnKQpFdXJvcGU8LWMoJ0FsYmFuaWEnLCdBbmRvcnJhJywnQXJtZW5pYScsJ0F1c3RyaWEnLCdBemVyYmFpamFuJywnQmVsYXJ1cycsJ0JlbGdpdW0nLCdCb3NuaWEgYW5kIEhlcnplZ292aW5hJywnQnVsZ2FyaWEnLCdDcm9hdGlhJywnQ3lwcnVzJywnQ3plY2ggUmVwdWJsaWMnLCdEZW5tYXJrJywnRXN0b25pYScsJ0ZpbmxhbmQnLCdGcmFuY2UnLCdHZW9yZ2lhJywnR2VybWFueScsJ0dyZWVjZScsJ0h1bmdhcnknLCdJY2VsYW5kJywnSXJlbGFuZCcsJ0l0YWx5JywnTGF0dmlhJywnTGllY2h0ZW5zdGVpbicsJ0xpdGh1YW5pYScsJ0x1eGVtYm91cmcnLCdNYWNlZG9uaWEnLCdNYWx0YScsJ01vbGRvdmEnLCdNb25hY28nLCdNb250ZW5lZ3JvJywnTmV0aGVybGFuZHMnLCdOb3J3YXknLCdQb2xhbmQnLCdQb3J0dWdhbCcsJ1JvbWFuaWEnLCdTYW4gTWFyaW5vJywnU2NvdGxhbmQnLCdTZXJiaScsJ1Nsb3Zha2lhJywnU2xvdmVuaWEnLCdTcGFpbicsJ1N3ZWRlbicsJ1N3aXR6ZXJsYW5kJywnVWtyYWluZScsJ0VuZ2xhbmQnLCdWYXRpY2FuIENpdHknLCdSZXB1YmxpYyBvZiBJcmVsYW5kJywnV2FsZXMnKQpOb3J0aF9hbWVyaWNhPC1jKCdBbnRpZ3VhIGFuZCBCYXJidWRhJywnQmFoYW1hcycsJ0JhcmJhZG9zJywnQmVsaXplJywnQ2FuYWRhJywnQ29zdGEgUmljYScsJ0N1YmEnLCdEb21pbmljYScsJ0RvbWluaWNhbiBSZXB1YmxpYycsJ0VsIFNhbHZhZG9yJywnR3JlbmFkYScsJ0d1YXRlbWFsYScsJ0hhaXRpJywnSG9uZHVyYXMnLCdKYW1haWNhJywnTWV4aWNvJywnTmljYXJhZ3VhJywnUGFuYW1hJywnU2FpbnQgS2l0dHMgYW5kIE5ldmlzJywnU2FpbnQgTHVjaWEnLCdTYWludCBWaW5jZW50IGFuZCB0aGUgR3JlbmFkaW5lcycsJ1RyaW5pZGFkIGFuZCBUb2JhZ28nLCdVbml0ZWQgU3RhdGVzJykKU291dGhfYW1lcmljYTwtYygnQXJnZW50aW5hJywnQm9saXZpYScsJ0JyYXppbCcsJ0NoaWxlJywnQ29sb21iaWEnLCdFY3VhZG9yJywnR3V5YW5hJywnUGFyYWd1YXknLCdQZXJ1JywnU3VyaW5hbWUnLCdVcnVndWF5JywnVmVuZXp1ZWxhJykKCmRmWywgY29udGluZW50Oj0gZGYkbmF0aW9uYWxpdHldCmRmIDwtIGRmICU+JSByZWxvY2F0ZShjb250aW5lbnQsIC5hZnRlciA9IG5hdGlvbmFsaXR5KQoKZGYkY29udGluZW50W2RmJGNvbnRpbmVudCAlaW4lIEFmcmljYSBdIDwtICJBZnJpY2EiCmRmJGNvbnRpbmVudFtkZiRjb250aW5lbnQgJWluJSBBbnRhcmN0aWNhIF0gPC0gIkFudGFyY3RpY2EiCmRmJGNvbnRpbmVudFtkZiRjb250aW5lbnQgJWluJSBBc2lhIF0gPC0gIkFzaWEiCmRmJGNvbnRpbmVudFtkZiRjb250aW5lbnQgJWluJSBFdXJvcGUgXSA8LSAiRXVyb3BlIgpkZiRjb250aW5lbnRbZGYkY29udGluZW50ICVpbiUgTm9ydGhfYW1lcmljYSBdIDwtICJOb3J0aF9hbWVyaWNhIgpkZiRjb250aW5lbnRbZGYkY29udGluZW50ICVpbiUgU291dGhfYW1lcmljYSBdIDwtICJTb3V0aF9hbWVyaWNhIgoKYGBgCiMjIEdyYWZpY29zIGRlIGFuYWxpc2UgZG9zIGRhZG9zClZhbGlkYcOnw6NvIGluY2lhbCBkZSBkYWRvcyAKYGBge3J9CnBsb3RfaW50cm8oZGYpCmBgYAoKCmBgYHtyfQpwbG90X21pc3NpbmcoZGYpCmBgYAoKSm9nYWRvcmVzIHBvciBwYWlzCmBgYHtyfQpwYWlzIDwtIGRmWywuTiwgYnk9bmF0aW9uYWxpdHldCmZyIDwtIGpvaW5Db3VudHJ5RGF0YTJNYXAoZEY9cGFpcywgam9pbkNvZGUgPSAiTkFNRSIsIG5hbWVKb2luQ29sdW1uID0gIm5hdGlvbmFsaXR5IiwgdmVyYm9zZSA9IEYpCgptYXBDb3VudHJ5RGF0YShtYXBUb1Bsb3QgPSBmcixuYW1lQ29sdW1uVG9QbG90ID0gIk4iLGNhdE1ldGhvZCA9ICJmaXhlZFdpZHRoIiwKICAgICAgICAgICAgICAgb2NlYW5Db2wgPSAic3RlZWxibHVlMSIsbWlzc2luZ0NvdW50cnlDb2wgPSAid2hpdGUiLAogICAgICAgICAgICAgICBtYXBUaXRsZSA9ICJKb2dhZG9yZXMgcG9yIHBhaXMiLAogICAgICAgICAgICAgICBhc3BlY3QgPSAidmFyaWFibGUiKSAKCmBgYAoKCiMjIEZpbHRyYW5kbyBzb21lbnRlIGF0YWNhbnRlcwpzZWxlY2lvbmFuZG8gb3MgYXRhY2FudGVzIApgYGB7cn0KRk9SV0FSRF9FVVJPUEUgPC0gZGYgJT4lCiAgZmlsdGVyKFBvc2l0aW9uID09ICJGb3J3YXJkIiAmIGNvbnRpbmVudCA9PSAiRXVyb3BlIikKCmhlYWQoRk9SV0FSRF9FVVJPUEUpCmBgYApzZWxlY2lvbmFuZG8gb3MgZ29sZWlyb3MgZGEgdmFyacOhdmVsIApgYGB7cn0KIy0tIGJhc2UgcGFyYSB2YWxpZGFjYW8KRk9SV0FSRF9OT1RfRVVST1BFIDwtIGRmICU+JQogIGZpbHRlcihQb3NpdGlvbiA9PSAiRm9yd2FyZCIgJiBjb250aW5lbnQgIT0gIkV1cm9wZSIpCgpoZWFkKEZPUldBUkRfTk9UX0VVUk9QRSkKYGBgCgojIyBSZW1vdmVuZG8gZGFkb3MgCnJlbW92ZW5kbyBvcyBOQXMgZSB2YXJpYXZlcyBuYW8gbnVtZXJpY2FzIGRhIGJhc2UgZGUgdHJlaW5vCmBgYHtyfQpGT1JXQVJEX0VVUk9QRSA8LSBGT1JXQVJEX0VVUk9QRSAlPiUgCiAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpICU+JQogIHNlbGVjdF9pZih+IGFueShpcy5udW1lcmljKC4pKSkKCmhlYWQoRk9SV0FSRF9FVVJPUEUpCmBgYApSZW1vdmVuZG8gb3MgTkFzIGUgdmFyaWF2ZXMgbmFvIG51bWVyaWNhcyBkYSBiYXNlIGRlIHZhbGlkYWNhbwpgYGB7cn0KZmlmYS4xOC5mdyA8LSBGT1JXQVJEX05PVF9FVVJPUEUgJT4lIAogIHNlbGVjdF9pZih+ICFhbnkoaXMubmEoLikpKSAlPiUKICBzZWxlY3RfaWYofiBhbnkoaXMubnVtZXJpYyguKSkpCgpoZWFkKGZpZmEuMTguZncpCmBgYAoKIyAgVHJlaW5hbWVudG8gZSBwcmVkaWNhbwoKIyMgQm94cGxvdCAKR3JhZmljbyBkZSBib3hwbG90IHNpbXBsZXMKYGBge3J9CmJveHBsb3QoRk9SV0FSRF9FVVJPUEUpCgoKYGBgCgojIyBDb3JyZWxhY2FvIAoKR3JhZmljbyBkZSBDb3JyZWxhY2FvIHNpbXBsZXMKYGBge3J9CmNvcnJNYXRyaXggPC0gY29yKEZPUldBUkRfRVVST1BFKQpjb3JycGxvdC5taXhlZChjb3JyTWF0cml4LCAKICAgICAgICAgICAgICAgbG93ZXIgPSAiZWxsaXBzZSIsIAogICAgICAgICAgICAgICB1cHBlciA9ICJudW1iZXIiLAogICAgICAgICAgICAgICB0bC5wb3MgPSAibHQiLAogICAgICAgICAgICAgICB0bC5jb2wgPSAiYmxhY2siLAogICAgICAgICAgICAgICBvcmRlcj0iaGNsdXN0IiwKICAgICAgICAgICAgICAgaGNsdXN0Lm1ldGhvZCA9ICJ3YXJkLkQiLAogICAgICAgICAgICAgICBhZGRyZWN0ID0gMykKCmBgYAojIyBEZWZpbmnDp8OjbyBkbyBtb2RlbG8gZGUgTUwgCgpNb2RlbG8gY29tIEZsb3Jlc3RhCmBgYHtyfQpzZXQuc2VlZCgxKQpyZWcudGVzdCA8LSByYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IGV1cl92YWx1ZSB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IEZPUldBUkRfRVVST1BFLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPTEwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBwcm94aW1pdHk9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhbEltcD1UUlVFKQpwbG90KHJlZy50ZXN0KQoKYGBgCiMgUHJlZGnDp8OjbwojIyBBdmFsaWHDp8OjbyAgClByZWRpw6fDo28gZG9zIHByZcOnb3MgZG9zIGdvbGVpcm9zCmBgYHtyfQpwcmVkaXRvID0gcHJlZGljdChyZWcudGVzdCwgRk9SV0FSRF9FVVJPUEUpCnByaW50KHBhc3RlKCJSMjogIiwgUjJfU2NvcmUocHJlZGl0bywgRk9SV0FSRF9FVVJPUEUkZXVyX3ZhbHVlKSApICkKcHJpbnQocGFzdGUoIk1TRTogIiwgTVNFKHByZWRpdG8sIEZPUldBUkRfRVVST1BFJGV1cl92YWx1ZSkgKSApCmBgYApgYGB7cn0KRk9SV0FSRF9FVVJPUEVbLCBwcmVkaXRvOj1wcmVkaXRvXQpGT1JXQVJEX0VVUk9QRSA8LSBGT1JXQVJEX0VVUk9QRSAlPiUgcmVsb2NhdGUocHJlZGl0bywgLmFmdGVyID0gZXVyX3ZhbHVlKQpoZWFkKEZPUldBUkRfRVVST1BFKQpgYGAKIyMgRGlzcGVyc8OjbwpBdmFsaWHDp2FvIGRlIGFjZXJ0byBYIGVycm8gZG8gbW9kZWxvIAoKYGBge3J9CkZPUldBUkRfRVVST1BFICU+JQogIG11dGF0ZShwcmVkaXRvID0gcHJlZGljdChyZWcudGVzdCwgLikpICU+JQogIHBsb3RfbHkoeCA9IH5ldXJfdmFsdWUsCiAgICAgICAgICB5PSB+cHJlZGl0bywKICAgICAgICAgIHR5cGU9J3NjYXR0ZXInLAogICAgICAgICAgbW9kZT0nbWFya2VycycsCiAgICAgICAgICB0ZXh0PX5wYXN0ZTAoIlJlYWwgdmFsdWU6ICIsIGN1cnJlbmN5KGV1cl92YWx1ZSwgc3ltYm9sPSfigqwnLCBkaWdpdHMgPSAwTCksIAogICAgICAgICAgICAgICAgICAgICAgICJcblByZWRpY3RlZCB2YWx1ZTogIiwgY3VycmVuY3kocHJlZGl0bywgc3ltYm9sPSfigqwnLCBkaWdpdHMgPSAwTCksIAogICAgICAgICAgICAgICAgICAgICAgICJcbkVycm9yOiAiLCAoZXVyX3ZhbHVlIC0gcHJlZGl0bykpLAogICAgICAgICAgbmFtZT0iRGlzcGVyc8OjbyIpICU+JQogIGFkZF9zZWdtZW50cyh4PTAsIHk9MCwgeGVuZCA9IDEwMDAwMDAwMCwgeWVuZCA9IDEwMDAwMDAwMCwgbmFtZT0iRXF1aWzDrWJyaW8iKQpgYGAKCiMjIEF2YWxpYcOnw6NvIElJCiMjIyBBdmFsaWHDp8OjbyBkb3MgcHJlw6dvcyAoYmFzZWFkbyBub3MgZGFkb3MgcXVlIG51bmNhIHZpdSBhbnRlcyEpClByZWRpw6fDo28gZG9zIHByZcOnb3MgZG9zIGdvbGVpcm9zCmBgYHtyfQpwcmVkaXRvID0gcHJlZGljdChyZWcudGVzdCwgZmlmYS4xOC5mdykKcHJpbnQocGFzdGUoIlIyOiAiLCBSMl9TY29yZShwcmVkaXRvLCBmaWZhLjE4LmZ3JGV1cl92YWx1ZSkgKSApCnByaW50KHBhc3RlKCJNU0U6ICIsIE1TRShwcmVkaXRvLCBmaWZhLjE4LmZ3JGV1cl92YWx1ZSkgKSApCmBgYApgYGB7cn0KZmlmYS4xOC5md1ssIHByZWRpdG86PXByZWRpdG9dCmZpZmEuMTguZncgPC0gZmlmYS4xOC5mdyAlPiUgcmVsb2NhdGUocHJlZGl0bywgLmFmdGVyID0gZXVyX3ZhbHVlKQpoZWFkKGZpZmEuMTguZncpCmBgYAojIyBEaXNwZXJzw6NvCmBgYHtyfQpmaWZhLjE4LmZ3ICU+JQogIG11dGF0ZShwcmVkaXRvID0gcHJlZGljdChyZWcudGVzdCwgLikpICU+JQogIHBsb3RfbHkoeCA9IH5ldXJfdmFsdWUsCiAgICAgICAgICB5PSB+cHJlZGl0bywKICAgICAgICAgIHR5cGU9J3NjYXR0ZXInLAogICAgICAgICAgbW9kZT0nbWFya2VycycsCiAgICAgICAgICB0ZXh0PX5wYXN0ZTAoIlJlYWwgdmFsdWU6ICIsIGN1cnJlbmN5KGV1cl92YWx1ZSwgc3ltYm9sPSfigqwnLCBkaWdpdHMgPSAwTCksIAogICAgICAgICAgICAgICAgICAgICAgICJcblByZWRpY3RlZCB2YWx1ZTogIiwgY3VycmVuY3kocHJlZGl0bywgc3ltYm9sPSfigqwnLCBkaWdpdHMgPSAwTCksIAogICAgICAgICAgICAgICAgICAgICAgICJcbkVycm9yOiAiLCAoZXVyX3ZhbHVlIC0gcHJlZGl0bykpLAogICAgICAgICAgbmFtZT0iRGlzcGVyc8OjbyIpICU+JQogIGFkZF9zZWdtZW50cyh4PTAsIHk9MCwgeGVuZCA9IDEwMDAwMDAwMCwgeWVuZCA9IDEwMDAwMDAwMCwgbmFtZT0iRXF1aWzDrWJyaW8iKQpgYGAKCiMgIFJlc3VsdGFkbwoKIyMgcmVzdWx0YWRvIGZpbmFsIGNvbSBvcyBnb2xlaXJvcyBlIHNldXMgdmFsb3JlcyBwcmVkaXRvcyAKCmBgYHtyfQpvdXRwdXQgPC0gRk9SV0FSRF9OT1RfRVVST1BFICU+JQogIHNlbGVjdChQb3NpdGlvbiwgbmFtZSwgZXVyX3ZhbHVlKSAKCm91dHB1dFssIGV1cl92YWx1ZSA6PSBjdXJyZW5jeShmaWZhLjE4LmZ3JGV1cl92YWx1ZSwgc3ltYm9sID0gJ+KCrCcsIGRpZ2l0cyA9IDBMKV0Kb3V0cHV0WywgJ1ByZcOnbyAiQ2FsY3VsYWRvIiAo4oKsKScgOj0gY3VycmVuY3koZmlmYS4xOC5mdyRwcmVkaXRvLCBzeW1ib2wgPSAn4oKsJywgZGlnaXRzID0gMEwpXQpvdXRwdXRbLCAnUG90ZW5jaWFsIFZhbG9yaXphw6fDo28gKOKCrCknIDo9IGN1cnJlbmN5KChmaWZhLjE4LmZ3JHByZWRpdG8gLSBmaWZhLjE4LmZ3JGV1cl92YWx1ZSksIHN5bWJvbD0n4oKsJywgZGlnaXRzID0gMEwpIF0Kb3V0cHV0WywgJ1BvdGVuY2lhbCBWYWxvcml6YcOnw6NvICglKScgOj0gKHBlcmNlbnQoKGZpZmEuMTguZnckcHJlZGl0byAtIGZpZmEuMTguZnckZXVyX3ZhbHVlKSAvIDEwMDAwMDAwMCkpIF0KCm91dHB1dCA8LSBvdXRwdXQgJT4lIAogIHJlbmFtZSgKICAgICdQb3Npw6fDo28nID0gUG9zaXRpb24sCiAgICAnSm9nYWRvcicgPSBuYW1lLAogICAgJ1ByZcOnbyBkZSBtZXJjYWRvJyA9IGV1cl92YWx1ZQogICkKCmhlYWQob3V0cHV0KQoKYGBgCgoqKioqKgojIFJvZGFww6kKIyMjIENhc2UgZGUgQWR2YW5jZWQgQW5hbHl0aWNzCioqKioqCipEYXZpZHNvbiBNaXphZWwgLSBTw6NvIFBhdWxvIC0gMjAyMSo=